
#include <BBuffer>
#include <BFolder>
#include <BEntry>
#include <BFile>
#include "member_BFile.h"

using namespace BWE;

#define member					(*(member_BFile*)_ptr)
#define member_allocate()		_ptr = new member_BFile(this)
#define member_release()		delete (member_BFile*)_ptr

bool BFile::Load(const BString& fileName, BByteArray& bytes)
{
	BFile file(fileName);
	if (file.open(IO_Read))
	{
		int size = file.size();
		bytes.resize(size);
		if (size == 0)
			return true;
		return file.read(bytes.data(), size);
	}
	return false;
}
bool BFile::Load(const BString& fileName, BBuffer& buffer)
{
	BFile file(fileName);
	if (file.open(IO_Read))
	{
		int size = file.size();
		buffer.resize(size);
		if (size == 0)
			return true;
		return file.read(buffer.bytes().data(), size);
	}
	return false;
}
bool BFile::Load(const BString& fileName, BString& text)
{
	BFile file(fileName);
	if (file.open(IO_ReadText))
	{
		int size = file.size();
		text.reserve(size);
		if (size == 0)
			return true;
		return file.read(text);
	}
	return false;
}
bool BFile::Save(const BString& fileName, const BByteArray& bytes)
{
	BFile file(fileName);
	if (file.open(IO_Write))
	{
		return file.write(bytes.data(), bytes.size());
	}
	return false;
}
bool BFile::Save(const BString& fileName, const BBuffer& buffer)
{
	BFile file(fileName);
	if(file.open(IO_Write))
	{
		return file.write(buffer.bytes());
	}
	return false;
}
bool BFile::Save(const BString& fileName, const BString& text)
{
	BFile file(fileName);
	if (file.open(IO_WriteText))
	{
		return file.write(text);
	}
	return false;
}

#ifdef linux

bool BFile::Exist(const BString& fileName)
{
	Attrib attrib = BEntry::Check(fileName);
	return attrib & Attrib_File;
}
bool BFile::Create(const BString& fileName)
{
	int fd = ::creat(fileName.cstr(), S_IRWXU | S_IRWXG | S_IRWXO);
	if(fd == -1)
	{
		printf("create file : %s", fileName.cstr());
		return false;
	}
	::close(fd);
	return true;
}
bool BFile::Remove(const BString& fileName)
{
	int result = ::remove(fileName.cstr());
	return result == 0;
}
bool BFile::Rename(const BString& fileName, const BString& newName)
{
	if (fileName.contain('/'))
	{
		BString newFileName = fileName.path();
		newFileName << '/' << newName;
		int result = ::rename(fileName.cstr(), newFileName.cstr());
		return result == 0;
	}
	int result = ::rename(fileName.cstr(), newName.cstr());
	return result == 0;
}

BFile::BFile()
{
	member_allocate();
}
BFile::BFile(const BString& fileName)
{
	member_allocate();
	member.fileName = fileName;
}
BFile::~BFile()
{
	if(member.handle)
		::close(member.handle);
	member_release();
}

const BString& BFile::fileName() const
{
	return member.fileName;
}

time_t BFile::modifyTime() const
{
	if(member.handle == 0)
		return 0;
	
	struct stat fs;
	::fstat(member.handle, &fs);
	return fs.st_mtime;
}
time_t BFile::changeTime() const
{
	if(member.handle == 0)
		return 0;
	
	struct stat fs;
	::fstat(member.handle, &fs);
	return fs.st_ctime;
}
time_t BFile::accessTime() const
{
	if(member.handle == 0)
		return 0;
	
	struct stat fs;
	::fstat(member.handle, &fs);
	return fs.st_atime;
}

int BFile::size() const
{
	if(member.handle == 0)
		return -1;
		
	struct stat fs;
	::fstat(member.handle, &fs);
	return fs.st_size;
}

bool BFile::open(IOMode mode)
{
	close();
	if(member.fileName.empty())
		return false;
	member.mode = mode;
	int flags = 0;
	switch(mode)
	{
	case IO_Read:
	case IO_ReadText:
		flags = O_RDONLY;
		break;
	case IO_Write:
	case IO_WriteText:
		flags = O_CREAT | O_WRONLY;
		break;
	case IO_Append:
	case IO_AppendText:
		flags = O_CREAT | O_APPEND;
		break;
	case IO_ReadWrite:
	case IO_ReadWriteText:
		flags = O_CREAT | O_RDWR;
		break;
	default:
		return false;
	}
	member.handle = ::open(member.fileName.cstr(), flags);
	if(member.handle > 0)
	{
		struct stat s;
		fstat(member.handle, &s);
		member.size = s.st_size;
		return true;
	}
	perror(member.fileName.cstr());
	member.handle = 0;
	return false;
}
bool BFile::open(const BString& fileName, IOMode mode)
{
	member.fileName = fileName;
	return open(mode);
}
bool BFile::close()
{
	if(member.handle)
	{
		if(::close(member.handle) == -1)
		{
			printf("close file failed : %s.\n", member.fileName.cstr());
		}
		member.handle = 0;
		member.fileName.clear();
		return true;
	}
	return false;
}
IOMode BFile::mode() const
{
    return member.mode;
}

bool BFile::isOpened() const
{
	return member.handle != 0;
}
bool BFile::flush()
{
	return false;
}

bool BFile::seek(int offset) const
{
	if(member.handle == 0)
		return false;
	int ret = ::lseek(member.handle, offset, SEEK_SET);
	if(ret == -1)
	{
		printf("lseek file error: %s.\n", member.fileName.cstr());
		return false;
	}
	return true;
}
bool BFile::wseek(int offset)
{
	if (!member.handle)
		return false;

	int size = member.size;
	if (offset > member.size)
	{
		int count = offset - member.size;
		if(count != ::write(member.handle, &count, count))
			return false;
		member.size += count;
	}
	else
	{
		int ret = ::lseek(member.handle, offset, SEEK_SET);
		if(ret == -1)
		{
			printf("lseek file failed : %s.\n", member.fileName.cstr());
			return false;
		}
	}
	return true;
}

int BFile::offset() const
{
	if(member.handle == 0)
		return -1;
	int ret = ::lseek(member.handle, 0, SEEK_CUR);
	if(ret == -1)
	{
		printf("lseek file failed : %s.\n", member.fileName.cstr());
	}
	return ret;
}

bool BFile::read(void* buffer, int size) const
{
	if(member.handle == 0)
		return -1;
	return ::read(member.handle, buffer, size);
}
bool BFile::write(const void* buffer, int size)
{
	if(member.handle == 0)
		return -1;
	return ::write(member.handle, buffer, size);
}

bool BFile::read(BString& text) const
{
	if (!member.handle)
		return false;
	text.reset();
	while (1)
	{
		char c = 0;
		if (0 == ::read(member.handle, &c, 1))
			break;
		text.append(c);
		if (c == 0)
			break;
	}
	return text.size();
}
bool BFile::write(const BString& text)
{
	if (!member.handle)
		return false;
	if (member.mode & IO_Text)
		return write(text.cstr(), text.size());
	return write(text.cstr(), text.size() + 1);
}

bool BFile::readCStr(char* cstr, int size) const
{
	if(member.handle == 0)
		return -1;
	for(int i = 0; i < size; i++)
	{
		if(::read(member.handle, cstr + i, 1) == 0)
		{
			cstr[i] = 0;
			return i;
		}
		if(cstr[i] == 0)
			return i;
	}
	return 0;
}
bool BFile::writeCStr(const char* cstr, int size)
{
	if(member.handle == 0)
		return -1;
	if(cstr == 0)
		return false;
	if (size < 0)
		size = strlen(cstr);
	if(cstr[size] != 0)
		return false;
	return ::write(member.handle, cstr, size + 1);
}

bool BFile::readLine(char* cstr, int size) const
{
	for (int i = 0; i < size; i++)
	{
		char c = 0;
		if (0 == ::read(member.handle, &c, 1))
			return i > 0;
		cstr[i] = c;
		if (c == '\n')
			return i > 0;
	}
	return false;
}
bool BFile::readLine(BString& str) const
{
	if (!member.handle)
		return false;
	str.reset();
	while (1)
	{
		char c = 0;
		if (0 == ::read(member.handle, &c, 1))
			break;
		str.append(c);
		if (c == '\n')
			break;
	}
	return !str.empty();
}

bool BFile::writeLine(const char* cstr, int size)
{
	if (!member.handle)
		return false;
	if (!cstr)
		return false;
	if (size == 0)
		return false;
	if (size < 0)
		size = (int)strlen(cstr);
	int count = 0;
	for (int i = 0; i < size; i++)
	{
		if (cstr[i] == '\n')
		{
			count = i + 1;
			break;
		}
	}
	if (count > 0)
	{
		if (count != ::write(member.handle, cstr, count))
			return false;
	}
	else
	{
		if (size != ::write(member.handle, cstr, size))
			return false;
		char seed = '\n';
		if (1 == ::write(member.handle, &seed, 1))
			return false;
	}
	return true;
}
bool BFile::writeLine(const BString& str)
{
	if (!member.handle)
		return false;
	int count = str.find('\n');
	if (count > 0 && count < str.size())
	{
		count += 1;
		if (count != ::write(member.handle, str.cstr(), count))
			return false;
	}
	else
	{
		if (str.size() != ::write(member.handle, str.cstr(), str.size()))
			return false;
		char seed = '\n';
		if (1 == ::write(member.handle, &seed, 1))
			return false;
	}
	return true;
}

#endif

#ifdef _WIN32

bool BFile::Exist(const BString& fileName)
{
	Attrib attrib = BEntry::Check(fileName);
	return attrib & Attrib_File;
}
bool BFile::Create(const BString& fileName)
{
	if (fileName.contain('/'))
	{
		BString path;
		int end = fileName.find('/');
		while (end < fileName.size())
		{
			fileName.substr(path, 0, end);
			if (false == BFolder::Exist(path))
			{
				if (FALSE == CreateDirectoryA(path.cstr(), 0))
				{
					return false;
				}
			}
			end = fileName.find('/', end + 1);
		}
	}
	HANDLE handle = CreateFileA(fileName.cstr(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, NULL, NULL);
	if (handle == INVALID_HANDLE_VALUE)
	{
		DWORD error = GetLastError();
		return false;
	}
	::CloseHandle(handle);
	return true;
}
bool BFile::Remove(const BString& fileName)
{
	int result = ::remove(fileName.cstr());
	return result == 0;
}
bool BFile::Rename(const BString& fileName, const BString& newName)
{
	if (fileName.contain('/'))
	{
		BString newFileName = fileName.path();
		newFileName << '/' << newName;
		int result = std::rename(fileName.cstr(), newFileName.cstr());
		return result == 0;
	}
	int result = std::rename(fileName.cstr(), newName.cstr());
	return result == 0;
}

BFile::BFile()
{
	member_allocate();
}
BFile::BFile(const BString& fileName)
{
	member_allocate();
	member.fileName = fileName;
}
BFile::~BFile()
{
	close();
	member_release();
}

void BFile::setFileName(const BString& fileName)
{
	if (member.fileName != fileName)
	{
		member.fileName = fileName;
		close();
	}
}
const BString& BFile::fileName() const
{
	return member.fileName;
}

bool BFile::isOpened() const
{
	if(member.fp)
		return true;
	return false;
}

bool BFile::open(IOMode mode)
{
	close();
	if(member.fileName.empty())
		return false;
	const char* flag = 0;
	switch(mode)
	{
	case IO_Read:
	case IO_ReadText:
		flag = "rb";
		break;
	case IO_Write:
	case IO_WriteText:
		flag = "wb";
		break;
	case IO_Append:
	case IO_AppendText:
		flag = "ab+";
		break;
	case IO_ReadWrite:
	case IO_ReadWriteText:
		flag = "wb+";
		break;
	default: 
		return false;
	}
	member.fp = ::fopen(member.fileName.cstr(), flag);
	if(member.fp)
	{
		member.mode = mode;
		::fseek(member.fp, 0, SEEK_END);
		int size = ::ftell(member.fp);
		member.size = ::ftell(member.fp);
		::fseek(member.fp, 0, SEEK_SET);
		return true;
	}
	return false;
}
bool BFile::open(const BString& fileName, IOMode mode)
{
	close();
	member.fileName = fileName;
	return open(mode);
}
bool BFile::close()
{
	if(member.fp)
	{
		if(std::fclose(member.fp) == 0)
		{
			member.fp = 0;
			member.size = 0;
			return true;
		}
	}
	return false;
}
bool BFile::flush()
{
	if(member.fp)
	{
		if(::fflush(member.fp) == 0)
			return true;
	}
	return false;
}

IOMode BFile::mode() const
{
	return member.mode;
}

bool BFile::seek(int offset) const
{
	if(member.fp)
	{
		if(::fseek(member.fp, offset, SEEK_SET) == 0)
			return true;
	}
	return false;
}
bool BFile::wseek(int offset)
{
	if (!member.fp)
		return false;

	int size = member.size;
	if (offset > member.size)
	{
		int count = offset - member.size;
		if(count != fwrite(&count, 1, count, member.fp))
			return false;
		member.size += count;
	}
	else
	{
		if (fseek(member.fp, offset, SEEK_SET))
			return false;
	}
	return true;
}

int BFile::offset() const
{
	if (!member.fp)
		return -1;
	return ::ftell(member.fp);
}
int BFile::size() const
{
	return member.size;
}

bool BFile::read(void* data, int size) const
{
	if (!data)
		return false;
	if (size > 0)
	{
		if (!member.fp)
			return false;
		if (size != fread(data, 1, size, member.fp))
			return false;
	}
	return true;
}
bool BFile::write(const void* data, int size)
{
	if (size > 0)
	{
		if (!member.fp || !data)
			return false;
		if (size != fwrite(data, 1, size, member.fp))
			return false;
		int offset = ftell(member.fp);
		if (offset > member.size)
			member.size = offset;
	}
	return true;
}

bool BFile::read(BString& text) const
{
	if (!member.fp)
		return false;
	text.reset();
	while (1)
	{
		char c = 0;
		if (0 == fread(&c, 1, 1, member.fp))
			break;
		text.append(c);
		if (c == 0)
			break;
	}
	return text.size();
}
bool BFile::write(const BString& text)
{
	if (!member.fp)
		return false;
	if (member.mode & IO_Text)
		return write(text.cstr(), text.size());
	return write(text.cstr(), text.size() + 1);
}

bool BFile::readCStr(char* cstr, int size) const
{
	if (!cstr || size <= 0)
		return false;
	char code = 0;
	for (int i = 0; i < size; i++)
	{
		if (!read(code))
		{
			cstr[0] = 0;
			return false;
		}
		cstr[i] = code;
		if (code == 0)
		{
			return true;
		}
	}
	return false;
}
bool BFile::writeCStr(const char* cstr, int size)
{
	if(!member.fp)
		return false;
	if(cstr == 0)
		return false;
	if (size < 0)
		size = (int)strlen(cstr);
	if(cstr[size] != 0)
		return false;
	return write(cstr, size + 1);
}

bool BFile::readLine(char* cstr, int size) const
{
	for (int i = 0; i < size; i++)
	{
		char c = 0;
		if (0 == fread(&c, 1, 1, member.fp))
			return i > 0;
		cstr[i] = c;
		if (c == '\n')
			return i > 0;
	}
	return false;
}
bool BFile::writeLine(const char* cstr, int size)
{
	if (!member.fp)
		return false;
	if (!cstr)
		return false;
	if (size == 0)
		return false;
	if (size < 0)
		size = (int)strlen(cstr);
	int count = 0;
	for (int i = 0; i < size; i++)
	{
		if (cstr[i] == '\n')
		{
			count = i + 1;
			break;
		}
	}
	if (count > 0)
	{
		if (count != fwrite(cstr, 1, count, member.fp))
			return false;
	}
	else
	{
		if (size != fwrite(cstr, 1, size, member.fp))
			return false;
		char seed = '\n';
		if (1 == fwrite(&seed, 1, 1, member.fp))
			return false;
	}
	return true;
}

bool BFile::readLine(BString& line) const
{
	if (!member.fp)
		return false;
	line.reset();
	while (1)
	{
		char c = 0;
		if (0 == fread(&c, 1, 1, member.fp))
			break;
		line.append(c);
		if (c == '\n')
			break;
	}
	return line.size();
}
bool BFile::writeLine(const BString& line)
{
	if (!member.fp)
		return false;
	int count = line.find('\n');
	if (count > 0 && count < line.size())
	{
		count += 1;
		if (count != fwrite(line.cstr(), 1, count, member.fp))
			return false;
	}
	else
	{
		if (line.size() != fwrite(line.cstr(), 1, line.size(), member.fp))
			return false;
		char seed = '\n';
		if (1 == fwrite(&seed, 1, 1, member.fp))
			return false;
	}
	return true;
}

#endif
